package com.example.fuzzy;

import java.util.*;



public class AutomataCadenas 
{
	private final int IF = 1;
	private final int THEN = 2;
	private final int IS = 3;
	private final int AND = 5;
	private final int NOP = 9;
	private final int RULE = 12;
	private final int UNDEFINED = 14;
	private final int LV = 16;

	private final int READY = 100;
	private final int LV_READ = 101;
	private final int IS_READ = 102;

	private final int COMPLETE_EVALUATION = 300;

	private final int RULE_READ = 400;
	private final int LABEL_READ = 401;
	private final int COMPLETE_EXECUTION = 402;

	private final int LABEL = 500;
	private final int EVALUATION = 501;
	private final int EXECUTION = 502;
	
	private double minimo=-999999;
    private double medio=-999999;
    private double maximo=-99999;
    
	private VariableLinguistica v = new VariableLinguistica();

	private Hashtable lvHash = null;
	private Hashtable controlHash = null;
	//private Hashtable labelWeightsHash = null;

	private boolean flagRuleFired = false;


	
	
public void evaluateRule(String rule) throws TesteoReglasExcepcion
{
	//Reset the flag
	flagRuleFired = false;
	
	//Split input string into tokens and return delimeters along with tokens
	java.util.StringTokenizer tokens = new java.util.StringTokenizer(rule.trim(),new String("( )"),true);

	//Result string
	String result = null;
	
	try
	{
		parseExpression(tokens); 
	}
	catch(TesteoReglasExcepcion e)
	{
		throw new TesteoReglasExcepcion("\nError parsing rule: "+rule+"\n"+e.getMessage());
	}


//	if(isRuleFired())	
//		return "Fired: "+result;
//	else				
//		return null;
}

public boolean isRuleFired() 
{
	return flagRuleFired;
}
 
private void parseExpression(java.util.StringTokenizer tokens)
			 throws TesteoReglasExcepcion
{

	int controlWord = NOP;

	int engineState = LABEL;

	int engineSubState = READY;

	VariableLinguistica lVariable = null;


    
    
	String mVariable = null;

	double accumulatedResult = 0.0;
	String currentToken = null;

	//Flags
	boolean flagAND = false;

	String changeRuleLabel = null;

//	String out = new String();

	//Process a rule
while(tokens.hasMoreTokens())
{
	//Reset control word
	controlWord = UNDEFINED;

	//Read next token
	currentToken = tokens.nextToken();

	//Add to the output string
	//out+=currentToken;

	//Check control words hastable
	Object temp = controlHash.get(currentToken);
	if(temp!=null)	
		controlWord=((Integer)temp).intValue();
	else
	{
		//Check LV hashtable
		temp = lvHash.get(currentToken);
		if(temp!=null)
			{
			controlWord=LV;
			lVariable = (VariableLinguistica)temp;
			}
		
	}

	//Switch according to the state/subState
	if(controlWord!=NOP)
	{
	switch(engineState)
	{
	case LABEL:
		switch(engineSubState)
		{
//LABEL:READY --------------------------------------------------------------------------------------------------
		case READY:
			switch(controlWord)
			{
				
				case IF:
					engineState = EVALUATION;
					break;
				
				default:
					throw new TesteoReglasExcepcion(" - A rule should start with a label of an 'if':");
			}
			break;

		}
		break;
	case EVALUATION:
		switch(engineSubState)
		{
//EVALUATION:READY ---------------------------------------------------------------------------------------------
		case READY:
			switch(controlWord)
			{
				case LV:
					engineSubState = LV_READ;
					break;
				case AND:
					if(!flagAND )
						flagAND = true;
					else
						throw new TesteoReglasExcepcion(" - Incorrect AND/OR operation");
					break;
			
			
				case THEN:
					engineState = EXECUTION;
					break;
				default:
					throw new TesteoReglasExcepcion(" - Incorrect operation: ");
			}
			break;
//EVALUATION:LV_READ -------------------------------------------------------------------------------------------
		case LV_READ:
			//the next item should be 'is' - everything else is an exception
			switch(controlWord)
			{
				case IS:
					engineSubState = IS_READ;
					break;
				default:
					throw new TesteoReglasExcepcion(" - only IS may be present at this place: ");
			}
			break;
//EVALUATION:IS_READ -------------------------------------------------------------------------------------------
		case IS_READ:
			//the next item may be a hedge or a membership function
			switch(controlWord)
			{
				
				case UNDEFINED:
					engineSubState = COMPLETE_EVALUATION;
					mVariable = currentToken;
					break;
				default:
					throw new TesteoReglasExcepcion(" - Incorrect operation after IS: ");
					
			}
			break;

			//EVALUATION:EXCEPTION -----------------------------------------------------------------------------------------
		default:
			throw new TesteoReglasExcepcion(" - Error in EVALUATION state: ");		
		}
		//EVALUATION:COMPLETE_EVALUATION -------------------------------------------------------------------------------
		if(engineSubState==COMPLETE_EVALUATION)
		
		
		//Rule evaluation mode
			{
			double tempResult = 0.0;

			//Complete evaluation
			tempResult = lVariable.is(mVariable);
			lVariable = null;

			if(tempResult == -1)
				throw new TesteoReglasExcepcion(" - Unable to perform fuzzy evaluation: ");

			
			//Check if doing AND or OR ---> !(AND||OR) = STORE
			if(!flagAND )
			{
				accumulatedResult = tempResult;
			}
			else
			{
				
				if(flagAND)
					{
				flagAND = false;
				if(accumulatedResult < tempResult)
					accumulatedResult = accumulatedResult;
					else 
						accumulatedResult = tempResult;
				
				
			//	accumulatedResult = accumulatedResult < tempResult ? accumulatedResult : tempResult;
					}
			
			}
			//Add to display string
//			String s = String.valueOf(tempResult);
//			out = out + "(" + s.substring(0,s.length() > 3 ? 4 : 3) + ") ";
			
			//}//end of mode switch
			
			//Switch subState
			engineSubState = READY;
		}
		break;
	case EXECUTION:
		switch(engineSubState)
		{

//EXECUTION: READY --------------------------------------------------------------------------------------------
		case READY:
			switch(controlWord)
			{
			case LV:
				engineSubState = LV_READ;
				break;
			//Nothing to do if an 'and' is found
			case AND:
				break;
			//Change a rule's weight
//			case RULE:
//				engineSubState = RULE_READ;
//				break;
			//Everything else generates exception
			default:
				throw new TesteoReglasExcepcion(" - Incorrect operation after THEN: ");
			}
			break;
//EXECUTION: RULE_READ ----------------------------------------------------------------------------------------
		case RULE_READ:
			//The next item should be a label
			switch(controlWord)
			{
			case UNDEFINED:
				changeRuleLabel = currentToken;
				engineSubState = LABEL_READ;
				break;
			default:
				throw new TesteoReglasExcepcion(" - A LABEL should follow RULE: ");
			}
			break;

//EXECUTION: LV_READ ------------------------------------------------------------------------------------------
		case LV_READ:
			//The next item must be IS - anything else generates an exception
			switch(controlWord)
			{
			case IS:
				engineSubState = IS_READ;
				break;
			default:
				throw new TesteoReglasExcepcion(" - Only IS can be present at this place: ");
			}
			break;
//EXECUTION: IS_READ ------------------------------------------------------------------------------------------
		case IS_READ:
			//The next item can be a HEDGE or a membership function
			switch(controlWord)
			{
			
			case UNDEFINED:
				mVariable = currentToken;
				engineSubState = COMPLETE_EXECUTION;
				break;
			default:
				throw new TesteoReglasExcepcion(" - Incorrect sequence after IS: ");
			}
			break;

		}//end of switch (EXECUTION)
		
//EXECUTION: COMPLETE_EXECUTION -------------------------------------------------------------------------------
		if(engineSubState ==COMPLETE_EXECUTION)
		
		
		//Rule evaluation mode
		{
		//A temporary variable to store results of hedging / negating
		double tempResult = accumulatedResult;

		
		
			//If something fired
			if(accumulatedResult > 0.0)
			{
				//If this is a weight change operation
//				if(changeRuleLabel!=null)
//				{
//					//Ensure that thisRuleLabel is not equal to changeRuleLabel
//					if(thisRuleLabel!=null && changeRuleLabel!=null && thisRuleLabel.equals(changeRuleLabel))
//						throw new TesteoReglasExcepcion(" - A LABEL cannot be assigned to a RULE that changes that label's WEIGHT: "+out);
//					//Fetch the label WEIGHT LV from the hash
//					lVariable = (VariableLinguistica)labelWeightsHash.get(changeRuleLabel);
//					if(lVariable==null)	
//						throw new TesteoReglasExcepcion(" - Unable to change WEIGHT for LABEL "+changeRuleLabel+" the LABEL hasn't yet been encountered: "+out);
//				}

				

				//Store result
				//Aqui tengo que ver si la variable que se va a defuzzificar es mayor que la otra no se mete.
				
				if (currentToken.equalsIgnoreCase("bajo") && tempResult>minimo ){
					v.setMinimoConjuntosBajo(tempResult);
					minimo=tempResult;
				}
				else if (currentToken.equalsIgnoreCase("medio") && tempResult>medio  ){
					v.setMinimoConjuntosMedio(tempResult);
					medio=tempResult;
				}
				else if (currentToken.equalsIgnoreCase("alto") && tempResult>maximo ){
					v.setMinimoConjuntosAlto(tempResult);
					maximo=tempResult;
				}
				
			//	lVariable.set(currentToken, tempResult);
				//Set fired flag
				flagRuleFired = true;

			}

			//Add to display string
//			String s = String.valueOf(tempResult);
//			out = out + "(" + s.substring(0,s.length() > 3 ? 4 : 3) + ") ";
			
		//}//end of mode switching

			//Switch engineSubState
			engineSubState = READY;

		}

		break;
	} //end if switch(engineState)
	} //end of if(controlWord!=NOP)
	
} //end of while

//return out;
}
 
//public ReglaFuzzy parseRule(String rule) throws TesteoReglasExcepcion 
//{
//	java.util.StringTokenizer tokens = new java.util.StringTokenizer(rule.trim(),new String("( )"),true);
//	ReglaFuzzy parsedRule = new ReglaFuzzy();
//
//	try
//	{
//		parseExpression(tokens, parsedRule); 
//	}
//	catch(TesteoReglasExcepcion e)
//	{
//		throw new TesteoReglasExcepcion("\nError parsing rule: "+rule+"\n"+e.getMessage());
//	}
//
//	return parsedRule;
//}

 
public void register(VariableLinguistica function) 
{
	this.lvHash.put(function.getLVName(),function);
}

public void reset() 
{
	//Reset fired rules
	flagRuleFired = false;
	 minimo=-999999;
     medio=-999999;
     maximo=-99999;
	for (java.util.Enumeration en = lvHash.elements() ; en.hasMoreElements() ;) 
		{
	    	((VariableLinguistica)en.nextElement()).reset();
	 	}
	
}


public AutomataCadenas() 
{
	super();
	
	//Create LV hashtable
	lvHash = new Hashtable();

	//Init. control controlHash hashtable
	controlHash = new Hashtable();

	controlHash.put(new String("if"), new Integer(IF));
	controlHash.put(new String("then"), new Integer(THEN));
	controlHash.put(new String("is"), new Integer(IS));
	controlHash.put(new String("and"), new Integer(AND));
	controlHash.put(new String(" "), new Integer(NOP));
	controlHash.put(new String("rule"), new Integer(RULE));
	
}

public AutomataCadenas(int i)
{}

public double getMinimo() {
	return minimo;
}

public double getMedio() {
	return medio;
}

public double getMaximo() {
	return maximo;
}



}




